<?php
if (!class_exists('AlfaControllerData')) {

	/**
	 * Simple configuration class based on the singleton design pattern
	 * @author Alecsandru Gabriel DINU
	 * @since Jun 12, 2008
	 */
	class AlfaControllerData {

		const default_configFile = "controller-default.xml";
		private $configFile = "controller.xml";
		private $xml; //SimpleXMLElement
		private $actions = array();
		private $cacheFilename = "controller_cache.php";
		private $devMode = false;


		public function __construct($devMode = false) {
			$this->devMode = $devMode;
			$config = AlfaConfiguration::getInstance();
			$this->cacheFilename = $config->getString('cache/dir',"/cache") .
					"/" . $config->getString('webapp/controller/cache-file', $this->cacheFilename);
			$this->configFile = $config->getString("webapp/controller/configFile", $this->configFile);
				
		}

		public function load() {
			if ($this->devMode) { // load the configuration every time
				$this->actions = $this->loadConfiguration();
				// and write the config to cache file
				$this->writeConfigToFile($this->actions);
			} else { //try to load the configuration from cache
				// Is our cache available? Recreate it!
				if(!is_readable($this->cacheFilename)) {
					$this->writeConfigToFile($this->loadConfiguration());
				}
				require_once $this->cacheFilename;
				$this->actions =& $GLOBALS['alfaControllerConfigurationData'];
			}
		}

		/**
		 * Forces the configuration to be reloaded
		 */
		public function reload() {
			$this->writeConfigToFile($this->loadConfiguration());
			require_once $this->cacheFilename;
			$this->actions =& $GLOBALS['alfaControllerConfigurationData'];
		}

		/**
		 * Loads config from both default config file and
		 * extended config file
		 */
		private function loadConfiguration() {
			$default = $this->readConfiguration(self::default_configFile);
			$extended = $this->readConfiguration($this->configFile);
				
			return array_merge($default, $extended);
		}

		private function readConfiguration($cfg) {
			$xmlStr = FileUtilities::getFileContent($cfg);
			$this->xml = new SimpleXMLElement($xmlStr);

			// Generates a the list with all the actions
			// from all the packages
			$actions = array();
			foreach ($this->xml->package as $package) {
				foreach ($package->action as $action) {
					if (StringUtils::isBlank($action["name"])) {
						die("Controller configuration is invalid");
					}

					$results = array();
					foreach ($action->result as $result) {
						$rname = (StringUtils::isNotBlank($result["name"])) ? trim($result["name"]) : "success";
						$results[$rname] = array (
							"type" => (StringUtils::isNotBlank($result["type"])) ? trim($result["type"]) : "",
							"value" => (string)$result,
						);
					}

					$aname = $package["namespace"] . "/" . $action["name"];
					$actions[$aname] = array (
						"name" => $aname,
						"namespace" => trim($package["namespace"]),
						"class" => (StringUtils::isNotBlank($action["class"])) ? trim($action["class"]) : "BaseAction",
						"method" => (StringUtils::isNotBlank($action["method"])) ? trim($action["method"]) : "execute",
						"results" =>  $results
					);
				}
			}

			return $actions;
		}

		/**
		 * Create cache file
		 * - Generates the array in PHP code and saves it as cache file
		 */
		private function writeConfigToFile($config) {
			/* Generate php cache file */
			$cache_content = "<?php\n// this is a automatically generated cache file.\n\n";
			foreach($config as $action_name => $action) {
				$cache_content .= "\$GLOBALS['alfaControllerConfigurationData']['".$action_name."'] = array(\n";
				$cache_content .= "\t'name' => '$action_name',\n";
				$cache_content .= "\t'namespace' => '".$action["namespace"]."',\n";
				$cache_content .= "\t'class' => '".$action["class"]."',\n";
				$cache_content .= "\t'method' => '".$action["method"]."',\n";
				$cache_content .= "\t'results' => array(\n";

				$result_index = 0;
				foreach($action["results"] as $result_name => $result) {
					if ($result_index++ > 0) {$cache_content .= ",";}
					$cache_content .= "\t\t'$result_name' => array(\n";
					$cache_content .= "\t\t\t'type' => '".$result["type"]."',\n";
					$cache_content .= "\t\t\t'value' => '".$result["value"]."'\n";
					$cache_content .= "\t\t)";
				}
				$cache_content .= "\t)\n);\n";

			}
			$cache_content .= "?>";
			if($cacheFilename_handle = fopen($this->cacheFilename, "w+")) {
				fwrite($cacheFilename_handle, $cache_content);
				/* Allow ftp users to access/modify/delete cache file, suppress chmod errors here */
				@chmod($this->cacheFilename, 0664);
			}
		}

		/**
		 * Gets an action from a specific namespace(package)
		 *
		 * @param string $actionName
		 * @param string $namespace
		 * @return array
		 */
		public function getActionFromNamespace($actionName, $namespace) {
			if (StringUtils::isNotBlank($namespace)) {
				$namespace = "/" . $namespace;
			}
				
			$actionName = "/" . $actionName . $namespace;
			if (array_key_exists ($actionName, $this->actions)) {
				return $this->actions[$actionName];
			}
			throw new AlfaControllerException("No action found : $actionName");
		}
			
		/**
		 * Gets an action. This method will look in all packages
		 * in order to find an action that corresponds
		 *
		 * @param string $actionName
		 * @return array
		 */
		public function getAction($actionName) {
			return $this->getActionFromNamespace($actionName, "");
		}

		/**
		 * Extracts the namespace from an action name
		 *
		 * @param string $actionName
		 * @return string
		 */
		public function getActionNamespace($actionName) {
			$action =& $this->getAction($actionName);
			return $action["namespace"];
		}

		/**
		 * Gets a result from an action base on its name
		 *
		 * @param array $action
		 * @param string $resultName
		 * @return array
		 */
		public function getActionResult($action, $resultName) {
			if (!(is_array($action))) {
				// assume it's the action name, and try to get it
				$action =& $this->getAction($action);
			}
				
			if (array_key_exists($resultName, $action["results"])) {
				return $action["results"][$resultName];
			}

			throw new AlfaControllerException("No result define for '$resultName' in action " . $action["name"]);
		}

	}
}